unit uDZButtons;

interface

uses
  SysUtils, Classes, Windows, Graphics, Controls, Messages, StdCtrls, ExtCtrls,
  uDZVclBase, uDZGraphics;

const
  UI_STATE_NORMAL = 0;
  UI_STATE_PRESSED = 1;
  UI_STATE_DISABLED = 2;
  UI_STATE_MOUSEOVER = 3;
  UI_STATE_DEFAULT = 4;
  
type
  TDzImageButton = class(TGraphicControl)
  private
    fMouseFlag: TMouseFlag;
    fPicture: TDzMarginBitmap;
    procedure PictureChanged(Sender: TObject; ID: TNotifyID);
    procedure CMMouseEnter(var msg: TMessage); message CM_MOUSEENTER;
    procedure CMMouseLeave(var msg: TMessage); message CM_MOUSELEAVE;
    procedure CMTextChange(var Msg: TMessage); message CM_TEXTCHANGED;
    function GetGlyphIndex: Integer;
    procedure AdjustBounds;
    procedure ChangePicture;
    procedure SetPicture(const Value: TDzMarginBitmap);
  protected
    function CanAutoSize(var NewWidth, NewHeight: Integer): Boolean; override;
    function GetPalette: HPALETTE; override;
    procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); override;
    procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); override;
  public
    constructor Create(Owner: TComponent); override;
    destructor Destroy; override;
    procedure Paint; override;
  published
    property Picture: TDzMarginBitmap read fPicture write SetPicture;
    property Action;
    property Align;
    property Anchors;
    property AutoSize;
    property Caption;
    property Enabled;
    property Font;
    property ShowHint;
    property Visible;
    property OnClick;
  end;

implementation

{ TDzImageButton }

procedure TDzImageButton.AdjustBounds;
var
  L, T: Integer;
begin
  if Assigned(fPicture) then
  begin
    if akRight in Anchors then L := Left - (fPicture.Width - Width)
    else L := Left;
    if akBottom in Anchors then T := Top - (fPicture.Height - Height)
    else T := Top;
    SetBounds(L, T, fPicture.Width, fPicture.Height);
  end;
end;

function TDzImageButton.CanAutoSize(var NewWidth, NewHeight: Integer): Boolean;
begin
  Result := True;
  if not (csDesigning in ComponentState) or (Assigned(fPicture) and
    (fPicture.Width > 0) and (Picture.Height > 0)) then
  begin
    if Align in [alNone, alLeft, alRight] then
      NewWidth := fPicture.Width;
    if Align in [alNone, alTop, alBottom] then
      NewHeight := fPicture.Height;
  end;
end;

procedure TDzImageButton.ChangePicture;
begin
  if AutoSize then AdjustBounds;
  Invalidate;
end;

procedure TDzImageButton.CMMouseEnter(var msg: TMessage);
begin
  Include(fMouseFlag, mfIn);
  ChangePicture;
end;

procedure TDzImageButton.CMMouseLeave(var msg: TMessage);
begin
  Exclude(fMouseFlag, mfIn);
  ChangePicture;
end;

procedure TDzImageButton.CMTextChange(var Msg: TMessage);
begin
  Invalidate;
end;

constructor TDzImageButton.Create(Owner: TComponent);
begin
  inherited;
  ControlStyle := ControlStyle + [csReplicatable, csPannable];
end;

destructor TDzImageButton.Destroy;
begin
  SetPicture(nil);
  inherited;
end;

function TDzImageButton.GetGlyphIndex: Integer;
begin
  if not Enabled then
    Result := UI_STATE_DISABLED
  else if mfDown in fMouseFlag then
    Result := UI_STATE_PRESSED
  else if mfIn in fMouseFlag then
    Result := UI_STATE_MOUSEOVER
  else
    Result := UI_STATE_NORMAL;
end;

function TDzImageButton.GetPalette: HPALETTE;
begin
  Result := 0;
  if Assigned(fPicture)  then
    Result := fPicture.Graphic.Palette;
end;

procedure TDzImageButton.MouseDown(Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  if Button = mbLeft then
  begin
    Include(fMouseFlag, mfDown);
    ChangePicture;
  end;
  inherited;
end;

procedure TDzImageButton.MouseUp(Button: TMouseButton; Shift: TShiftState;
  X, Y: Integer);
begin
  inherited;
  if Button = mbLeft then
  begin
    Exclude(fMouseFlag, mfDown);
    ChangePicture;
  end;
end;

procedure TDzImageButton.Paint;
var
  text: string;
  size: TSize;
  index: Integer;
begin
  inherited;
  if Assigned (fPicture) then
  begin
    index := GetGlyphIndex;
    if index >= fPicture.GlyphCount then index := 0;
    picture.DrawTo(index, Canvas, 0, 0, Width, Height);
  end;
  text := Caption;
  if text <> '' then
  begin
    Canvas.Font := Self.Font;
    Canvas.Brush.Style := bsClear;
    size := Canvas.TextExtent(text);
    Canvas.TextOut((Width - size.cx) div 2,
      (Height - size.cy) div 2, text);
  end;
end;

procedure TDzImageButton.PictureChanged(Sender: TObject; ID: TNotifyID);
begin
  if ID = niDestroy then
  begin
    if Sender = fPicture then SetPicture(nil);
  end
  else if ID = niChange then
  begin
    if (Sender = fPicture) and AutoSize then
      AdjustBounds;
  end;
end;

procedure TDzImageButton.SetPicture(const Value: TDzMarginBitmap);
begin
  if fPicture <> Value then
  begin
    if Assigned(fPicture) then
      fPicture.RemoveOnChangeListener(Self.PictureChanged);
    fPicture := Value;
    if Assigned(fPicture) then
      fPicture.AddOnChangeListener(Self.PictureChanged);
    ChangePicture;
  end;
end;

end.

